#include "ServerConnection.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <regex.h>
#include <string.h>
#include "stdGNUTDefs.h"
#include "Packet.h"
#include <list.h>
#include "ReceiveWindow.h"

#ifdef USEMNL
#include "MNLClient.h"
#endif

ServerConnection::ServerConnection(int port, int wndwSize)
{
#ifdef USEMNL
	// Setup the MNL crap
	if( MNLClientInit() != 1 )
	{
		// Death to smoochie
		printf("ERROR -- MNLClientInit() failure\n");
		exit(1);
	}
#endif

	// Setup the receive window
	myRcvWindow = ReceiveWindow(wndwSize);


	// Setup a place to store received messages
	// A received message can be no larger than 500 bytes
	// anything beyond this amount in the payload will be 
	// discarded
	myMsgBufLen = sizeof(GNUT_Header_t) + MSS;
	myMsgBuf = (char*)malloc((myMsgBufLen)*sizeof(char));
	myRcvdPacket = Packet();

	// Setup the standard ACK packet that we will send
	// when we need to acknowledge data
	myAckPacket = Packet();

	// Setup myAddr structure
	myAddr.sin_family = AF_INET;
	myAddr.sin_port = htons(port);
	myAddr.sin_addr.s_addr  = htonl(INADDR_ANY);
	memset(myAddr.sin_zero, '\0', sizeof(myAddr.sin_zero));


	dstAddr.sin_family = AF_INET;
	dstAddr.sin_port = htons(0);
	dstAddr.sin_addr.s_addr = htonl(0);
	memset(dstAddr.sin_zero, '\0', sizeof(dstAddr.sin_zero));

	// Setup connection variables
	// we want to make sure that O_NONBLOCK is NOT set... I think this is default...
	if((fdSend = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
		perror("socket");
		exit(1);
	}else if((fdRecv = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
		perror("socket");
		exit(1);
	}else if(setsockopt(fdRecv, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
		perror("setsockopt");
		exit(1);
	}else if(bind(fdRecv, (struct sockaddr *)&myAddr, sizeof(myAddr)) == -1) {
		perror("bind");
		exit(1);
	}

}

/**
 * returns 1 on success, -1 if failed
 */
int ServerConnection::accept()
{
	verbose ? printf("Waiting for a SYN\n"):1;
	bool done = false;

	struct sockaddr_in from;
	unsigned int fromLength = sizeof(struct sockaddr_in);

	while( !done && ((myBytesRcvd = recvfrom(fdRecv, myMsgBuf, myMsgBufLen, 0, (struct sockaddr *)&from, (socklen_t*)&fromLength)) > 0) )
	{
		myRcvdPacket.setPacket(myMsgBuf, myBytesRcvd);

		verbose ? printf("Received the following message:\n"):1;
		verbose ? myRcvdPacket.printPacket():1;

		if( myRcvdPacket.isSYN() && myRcvdPacket.checkCheckSum() )
		{			
			verbose ? printf("The message is a syn and the checksum is ok\n"):1;

			dstAddr.sin_port = htons(myRcvdPacket.getSrcPort());
			dstAddr.sin_addr.s_addr = from.sin_addr.s_addr;
			dstAddrLen = fromLength;

			myAckPacket.setPacket(	ntohs(((sockaddr_in)myAddr).sin_port), 
					myRcvdPacket.getSrcPort(), 
					myRcvdPacket.getSeqNo() + 1,
					myRcvWindow.getRemainingWindowSize(),
					ACK,
					NULL,
					0	);

			verbose ? printf("Built the following ack:\n"):1;
			verbose ? myAckPacket.printPacket():1;

			// send the ack
			if( sendPacket(myAckPacket) == -1 )
			{
				printf("ERROR -- sendpacket failed!\n");
				return -1;
			}

			myRcvWindow.setISN(myRcvdPacket.getSeqNo());

			done = true;
		}
	}

	if( done ) 
	{
		return 1;
	}
	else
	{
		verbose ? printf("recvfrom had a problem\n"):1;	
		verbose ? printf("myBytesRcvd: %d\n", myBytesRcvd):1;
		perror("recvfrom");
		return -1;
	}
}

/**
 * send() - Send a packet (unreliable).  Need to check for ACKs
 * 
 * Return values -
 * 		1 for success
 * 		-1 for failure
 */
int ServerConnection::sendPacket(Packet p) {
	int ipError = 0;
	char *buffer = p.toBytes();
	int sendAmt = p.getSize();

	int justSent = 0;
	int retVal = 1;

	while(sendAmt > 0) {
#ifdef USEMNL
		justSent = 	MNL_sendto(fdSend, buffer, sendAmt, 0, (struct sockaddr *)&dstAddr, sizeof(dstAddr),
				p.getSeqNo(), p.getPayloadLength(), p.getAdvWindow(), &ipError);
#else
		justSent = sendto(fdSend, buffer, sendAmt, 0, (struct sockaddr*)&dstAddr, sizeof(dstAddr));
#endif

		if(justSent < 0) {
			perror("sendto failed");
			retVal = -1;
			break;
		}
		if(ipError == 1) {
			printf("ERROR -- ipError is 1\n");
			retVal = -1;
			break;
		}
		sendAmt -= justSent;
	}
	p.freeBytes();
	return retVal;
}

/**
 * Takes in a file descriptor pointing to a file on disk
 * that we are to write the received data to. We assume 
 * this file is already open and ready for writing. We do
 * not close this file when we are done.
 * 
 * return bytes written to file on success.
 * 			return -1 on error
 */
int ServerConnection::recv(int fd)
{
	verbose ? printf("entering receive\n"):1;

	myRcvWindow.setTargetFile( fd );
	sockaddr_in tempDstAddr;
	socklen_t tempDstAddrLen;

	while( myRcvWindow.isAlive() ) 
	{
		// TODO: not sure if I should be returning the number of bytes written here??
		if( (myBytesRcvd = recvfrom(fdRecv, myMsgBuf, myMsgBufLen, 0, (struct sockaddr *)&tempDstAddr, &tempDstAddrLen)) == -1 )
			return -1;

		myRcvdPacket.setPacket(myMsgBuf, myBytesRcvd);

		verbose ? printf("Received packet:\n"):1;
		verbose ? myRcvdPacket.printPacket():1;

		bool goodPacket = true;

		// make sure it's a packet we are interested in
		goodPacket = goodPacket && myRcvdPacket.checkCheckSum();
		goodPacket = goodPacket && !(myRcvdPacket.isACK());
		goodPacket = goodPacket && (myRcvdPacket.getSrcPort() == (unsigned short)(ntohs(dstAddr.sin_port)));
		goodPacket = goodPacket && (myRcvdPacket.getDstPort() == (unsigned short)(((sockaddr_in)myAddr).sin_port));

		if( goodPacket && myRcvdPacket.isSYN() )
		{
			// if we get a good packet and it's a SYN, then we need to resend the ACK to establish the connection
			verbose ? printf("The message is a syn and the checksum is ok\n"):1;

			myAckPacket.setPacket(	ntohs(((sockaddr_in)myAddr).sin_port), 
					myRcvdPacket.getSrcPort(), 
					myRcvdPacket.getSeqNo() + 1,
					myRcvWindow.getRemainingWindowSize(),
					ACK,
					NULL,
					0	);

			verbose ? printf("Built the following ack:\n"):1;
			verbose ? myAckPacket.printPacket():1;

			// send the ack
			if( sendPacket(myAckPacket) == -1 )
			{
				printf("ERROR -- sendpacket failed!\n");
				return -1;
			}

			myRcvWindow.setISN(myRcvdPacket.getSeqNo());
			myRcvWindow.clear();
			
		} else if( goodPacket ) 
		{
			verbose ? printf("Packet is for us\n"):1;
			verbose ? printf("Adding packet to receive window\n"):1;

			myRcvWindow.addPacket(myRcvdPacket);

			verbose ? myRcvWindow.printWindowState():1;

			myAckPacket.setAckNo(myRcvWindow.getLeftWindowEdge() + 1);
			myAckPacket.setAdvWindow(myRcvWindow.getRemainingWindowSize());

			verbose ? printf("Built the following ack:\n"):1;
			verbose ? myAckPacket.printPacket():1;

			if( sendPacket(myAckPacket) == -1 )
			{
				printf("ERROR -- sendpacket failed!\n");
				return -1;
			}
		}
	}

	return myRcvWindow.getBytesWritten();
}

void ServerConnection::closeConnection()
{
	closesocket(fdSend);
	closesocket(fdRecv);
	myRcvWindow.clear();
}

ServerConnection::~ServerConnection()
{
	free(myMsgBuf);
}
